package org.jeecgframework.tinydao.factory;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.log4j.Logger;

public class PackagesToScanUtil {
 private static final Logger logger = Logger.getLogger(PackagesToScanUtil.class);
 
 private static final String SUB_PACKAGE_SCREEN_SUFFIX = ".*";
 private static final String SUB_PACKAGE_SCREEN_SUFFIX_RE = ".\\";//替换使用
 
 
 public static Set<Class<?>> getClasses(String pack){
	 Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
	 
	 //是否循环迭代
	 boolean recursive = false;
	 String[] packArr = {};
	 
	 if(pack.lastIndexOf(SUB_PACKAGE_SCREEN_SUFFIX)!=-1){
		 packArr = pack.split(SUB_PACKAGE_SCREEN_SUFFIX_RE);
		 if(packArr.length>1){
			 //需要匹配中间的任意层包
			 pack = packArr[0];
			 for(int i =0 ;i< packArr.length;i++){
				 packArr[i] = packArr[i].replace(SUB_PACKAGE_SCREEN_SUFFIX.substring(1), "");
			 }
		 }else{
			 pack = pack.replace(SUB_PACKAGE_SCREEN_SUFFIX, "");
		 }
		 
		 recursive=true;
	 }
	 
	 //获取报的名字并进行替换
	 String packageName = pack;
	 String packageDirName = packageName.replace('.', '/');
	 //定义一个枚举的集合并进行循环来处理这个目录下的tings
	 Enumeration<URL> dirs;
	 
	  try {
		dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
		//循环迭代下去
		while(dirs.hasMoreElements()){
			//
			URL url = dirs.nextElement();
			
			String protocol = url.getProtocol();
			
			if("file".equals(protocol)){
				logger.debug("------------扫描文件类型--------------");
				//获取包的物理路径
				String filePath = URLDecoder.decode(url.getFile(),"UTF-8");
				
				//以文件的方式扫描整个包下面的文件并添加到集合里
				findAndAddClassesInPackageByFile(packageName,packArr,filePath,recursive,classes);
			}else if("jar".equals("protocol")){
				//以jar的方式扫描真个包下面的类并添加到集合里
				findAndAddClassesInPackageByJarFile(packageName,packArr,url,packageDirName,recursive,classes);
			}
			
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
	 
	 
	 return classes;
 }



/** 
* @Title: findAndAddClassesInPackageByJarFile 
* @Description: 以jar文件的形式获取包下的所有Class 
* @param @param packageName
* @param @param packArr
* @param @param url
* @param @param packageDirName
* @param @param recursive
* @param @param classes    设定文件 
* @return void    返回类型 
* @throws 
*/
private static void findAndAddClassesInPackageByJarFile(String packageName,
		String[] packArr, URL url, String packageDirName, boolean recursive, Set<Class<?>> classes) {
	//如果是jar包文件
	logger.debug("------------- jar 类型的扫描--------");
	//定义一个jarFile
	JarFile jar;
	try{
		//获取jar
		jar = ((JarURLConnection)url.openConnection()).getJarFile();
		//从此jar包里得到类的枚举
		Enumeration<JarEntry> entries = jar.entries();
		//同样进行循环迭代
		while(entries.hasMoreElements()){
			//获取jar里的一个实体 可以是目录和一些jar包里的其他玩家 如META-INF等文件
			JarEntry entry = entries.nextElement();
			String name = entry.getName();
			//如果是以/开头的
			if(name.charAt(0) =='/'){
				//获取后面的字符串
				name = name.substring(1);
			}
			//如果前半部分和定义的报名相同
			if(name.startsWith(packageDirName)){
				int idx = name.lastIndexOf('/');
				//如果以“/”结尾的是一个包
				if(idx != -1){
					//获取报名 把“/” 替换成“.”
					packageName = name.substring(0,idx).replace('/', '.');
				}
				//如果可以迭代下去并且是个包
				if((idx !=-1)|| recursive){
					//如果是个.class文件而不是目录
					if(name.endsWith(".class")&&!entry.isDirectory()){
						//去掉后面的“.class”获取真正的类名
						String className = name.substring(packageName.length()+1,name.length()-6);
						
						try{
							//添加到classes
							boolean flag = true;
							if(packArr.length>1){
								for(int i =1;i<packArr.length;i++){
									if(packageName.indexOf(packArr[i])<=-1){
										flag = flag&&false;
									}else {
										flag = flag && true;
									}
								}
							}
							if(flag){
								classes.add(Class.forName(packageName+'.'+className));
							}
						}catch(ClassNotFoundException e){
							logger.error("添加用户自定义视图类错误找不到此类.class文件");
							e.printStackTrace();
						}
					}
				}
			}
		}
		
	}catch(IOException e){
		logger.error("在扫描用户定义视图时从jar包获取文件出错");
		e.printStackTrace();
	}
	
}


/** 
* @Title: findAndAddClassesInPackageByFile 
* @Description: 以文件的形式来获取包下的所有Class
* @param @param packageName
* @param @param packArr
* @param @param filePath
* @param @param recursive
* @param @param classes    设定文件 
* @return void    返回类型 
* @throws 
*/
private static void findAndAddClassesInPackageByFile(String packageName,
		String[] packArr, String filePath, final boolean recursive,
		Set<Class<?>> classes) {
	 //获取此包的目录 建立一个 File
	File dir = new File(filePath);
	//如果不存在或者也不是目录就直接返回
	if(!dir.exists()||!dir.isDirectory()){
		return;
	}
	//如果存在就获取包下的所有文件包括目录
	File[] dirfiles = dir.listFiles(new FileFilter(){
		//自定义过滤规则 如果可以循环（包含子目录）或者是以。class结尾的文件
		@Override
		public boolean accept(File pathname) {
			return (recursive && pathname.isDirectory())||(pathname.getName().endsWith(".class"));
		}
		
	});
	
	//循环所有文件
	for(File file:dirfiles){
		//如果是目录就继续扫描
		if(file.isDirectory()){
			findAndAddClassesInPackageByFile(packageName+"."+file.getName(), packArr, filePath, recursive, classes);
		}else{
			//如果是java类文件 去掉后面的.class只留下类名
			String className = file.getName().substring(0,file.getName().length()-6);
			//添加到集合里面
			try {
			String classUrl = packageName+'.'+className;
			//判断是否一点开头
			if(classUrl.startsWith(".")){
				classUrl = classUrl.replaceFirst(".", "");
			}
			
			//判断这个类是否真的存在
			boolean flag = true;
			if(packArr.length>1){
				for(int i =1;i<packArr.length;i++){
					if(classUrl.indexOf(packArr[i])<=-1){
						flag = flag && false;
						
					}else {
						flag = flag && true;
					}
					
				}
			}
			if(flag){
				
					classes.add(Thread.currentThread().getContextClassLoader().loadClass(classUrl));
				
			}
			} catch (ClassNotFoundException e) {
				logger.error("添加用户自定义视图类错误 找不到此类的.class文件");
				e.printStackTrace();
			}
		}
	}
	
}
}
